<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>messageChannel 模拟 requestIdleCallback</title>
  </head>
  <body>
    <script>
      function sleep(delay) {
        for (let now = Date.now(); Date.now() - now < delay; ) {}
      }
      let allStart = 0;
      const works = [
        () => {
          allStart = Date.now();
          console.log("this first work start");
          sleep(20);
          console.log("this first work end");
        },
        () => {
          console.log("this second work start");
          sleep(10);
          console.log("this second work end");
        },
        () => {
          console.log("this third work start");
          sleep(10);
          console.log("this third work end");
          console.log(Date.now() - allStart);
        },
      ];
      /* 模拟实现requestIdleCallback 开始 */
      let channel = new MessageChannel();
      let activeFrameTime = 1000 / 60;
      let frameDeadline; // 每帧的截止时间
      let pendingCallback;
      let timeRemaining = () => frameDeadline - performance.now();
      channel.port2.onmessage = function () {
        let currentTime = performance.now();
        // 如果帧的截止时间，小于当前时间，说明已经过期
        let didTimeout = frameDeadline <= currentTime;
        if (didTimeout || timeRemaining() > 0) {
          if (pendingCallback) {
            // 原生requestIdleCallback回调函数中的2个参数
            pendingCallback({ didTimeout, timeRemaining });
          }
        }
      };
      window.requestIdleCallback = (callback, options) => {
        requestAnimationFrame((rafTime) => {
          console.log("rafTime", rafTime);
          frameDeadline = rafTime + performance.now();
          pendingCallback = callback;
          channel.port1.postMessage("send");
        });
      };
      /* 模拟实现requestIdleCallback 结束 */
      requestIdleCallback(workLoop, { timeout: 1000 });
      function workLoop(deadline) {
        console.log(`this frame remainTime is ${deadline.timeRemaining()}`);
        // deadline.timeRemaining() > 0 说明还有剩余时间
        // deadline.didTimeout说明任务已经过期，则必须执行
        while (
          (deadline.timeRemaining() > 0 || deadline.didTimeout) &&
          works.length > 0
        ) {
          performUnitOfWork();
        }
        if (works.length > 0) {
          console.log(
            `this frame remainTime is ${deadline.timeRemaining()}，时间片已经到期，等待下次调度`
          );
          window.requestIdleCallback(workLoop, { timeout: 1000 });
        }
      }
      function performUnitOfWork() {
        works.shift()();
      }
    </script>
  </body>
</html>
